#@title Imports
import scipy.io as sio
'''
This statement imports the scipy.io module and assigns it an alias sio.
The scipy.io module provides functions for working with various
file formats used in scientific computing, such as MATLAB files.
'''
import scipy.misc as spm
''' This statement imports the scipy.misc module and assigns it an alias spm.
The scipy.misc module provides various utility functions for image manipulation
and processing, such as reading and saving images.
'''
from scipy import ndimage
'''
This statement imports the ndimage module from the scipy package.
The ndimage module provides functions for multi-dimensional image processing,
such as filtering, interpolation, and morphology operations.
'''
import cv2 as cv
import os
import numpy as np
import matplotlib.image as plt
from IPython.display import Image, display
%matplotlib inline
import pandas as pd
from scipy import stats, integrate
import seaborn as sns
sns.set(color_codes=True)
from collections import Counter
import torch
from torch import nn
import matplotlib.pyplot as plt
import torch.nn.functional as F
import torchvision
from torch.utils.data import Dataset, DataLoader
from torch.autograd import Variable
from torch.utils.data.sampler import SubsetRandomSampler
import glob
import albumentations as A
from albumentations.core.composition import OneOf
from plotly import express as px
!pip install --upgrade plotly;
!pip install torchmetrics;
!pip install albumentations==0.4.6;
from torchmetrics import Accuracy, Precision, Recall
#@title All Dirs Paths { display-mode: "form",run: "auto" }
class CFG:
IMG_DIR_IMDB = os.path.join('/content/imdb_crop') #@param {type:"string"}
MAT_FILE_IMDB = os.path.join('/content/imdb_crop/imdb.mat')#@param {type:"string"}
#@markdown inside wiki data there is no celeb_id so i cannot take from that the image we wanted
IMG_DIR_WIKI = os.path.join('/content/wiki_crop')#@param {type:"string"}
MAT_FILE_WIKI = os.path.join('/content/wiki_crop/wiki.mat')#@param {type:"string"}
#@title Functions
def readData(num=0):
if num == 0:
b = pd.read_csv('/content/celebs4face_detection.csv')
else:
b = pd.read_csv('/content/faces_meta_data.csv')
b.replace('', np.NaN, inplace=True)
return b
def create_subplots(images:list, titles:list = None,wantAxis = False,figsize=(12, 8)):
fig = plt.figure(figsize=figsize)
num_subplots = len(images)
for i in range(num_subplots):
plt.subplot(1, num_subplots, i+1)
plt.imshow(images[i], cmap="gray")
plt.axis("off" if wantAxis ==False else "on")
plt.title(titles[i] if titles != None else i)
plt.show()
def show(image,WantAxis=True,text="Image"):
create_subplots([image],[text],WantAxis,(6,4))
def makeFromDictPandas(theDict):
return pd.DataFrame(theDict)
def makeDFtoCSV(theDF,name):
theString = str(name)+'.csv'
theDF.to_csv(theString, index=False)
def unpack_path(path):
return os.path.join(CFG.IMG_DIR_IMDB, path[0])
def unpack_name(name):
return name[0]
def getDataReady():
mat_struct = sio.loadmat(CFG.MAT_FILE_IMDB)
data_set = [data[0] for data in mat_struct['imdb'][0, 0]]
keys = ['dob',
'photo_taken',
'full_path',
'gender',
'name',
'face_location',
'face_score',
'second_face_score',
'celeb_names',
'celeb_id']
imdb_dict = dict(zip(keys, np.asarray(data_set)))
del imdb_dict['dob']
del imdb_dict['celeb_names']
imdb_dict['full_path'] = [unpack_path(path) for path in imdb_dict['full_path']]
imdb_dict['name'] = [unpack_name(name) for name in imdb_dict['name']]
nf = makeFromDictPandas(imdb_dict)
nf.replace('', np.NaN, inplace=True)
nf = nf[~np.isinf(nf['face_score'])]
nf = nf[nf['face_score'].notna()]
nf = nf[nf['second_face_score'].isna()]
nf = nf.drop(['second_face_score'], axis=1)
tf = readData(0)
arr = tf['celeb_id'].tolist()
nf = nf[nf['celeb_id'].isin(arr)]
return nf
First, the labels, which was not easily obtained. The meta data is stored separately and in a .mat file. (Yes, matlab)!
Luckily we can use the scipy.io.loadmat to load the .mat file to python accessible (kind of) format. We can access the dob by some proper indexing.
#@title clean directory { run: "auto" }
import shutil
def cleanDirectorys(path):
if os.path.exists(f'{path}'):
shutil.rmtree(f'{path}')
#@markdown enter which directory to clean from files
theDir = '' #@param {type:"string"}
cleanDirectorys(theDir)
#@title Install the data ? { run: "auto" }
def installData():
!wget https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/static/wiki_crop.tar
!tar -xf wiki_crop.tar
!wget https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/static/imdb_crop.tar
!tar -xf imdb_crop.tar
install_Data = False #@param {type:"boolean"}
if install_Data:
installData()
#@title Get ready the data ? { run: "auto" }
ok = True #@param {type:'boolean'}
if ok:
nf = getDataReady();
<ipython-input-56-bce360e20f91>:42: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray. imdb_dict = dict(zip(keys, np.asarray(data_set))) /usr/local/lib/python3.10/dist-packages/pandas/core/missing.py:95: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison new_mask = arr == x
#@title The data is clean and ready.
nf.info();
#@title Print images
all_ids = list(set(nf['celeb_id']))
num_images = 5
fig, axs = plt.subplots(len(all_ids), num_images, figsize=(36, 22))
sum_images_for_swapping = []
for i, value in enumerate(all_ids):
full_paths = nf.loc[nf['celeb_id'] == value, 'full_path'].tail(num_images)
names = nf.loc[nf['celeb_id'] == value, 'name'].tail(num_images) # Retrieve corresponding names
for j, (path, name) in enumerate(zip(full_paths, names)):
image = plt.imread(path)
axs[i, j].imshow(image, cmap="gray")
axs[i, j].axis("off")
axs[i, j].set_title(f"Name: {name}") # Set the title as the name
sum_images_for_swapping.append(image)
plt.tight_layout()
plt.show()
#@title FaceDataset
class FaceDataset(Dataset):
def __init__(self,img_paths,label ,transforms = None):
self.img_paths = img_paths
self.label = label
self.transform = transforms
def __getitem__(self, idx):
image_path = self.img_paths[idx]#gets the image in place idx from the diractory
image_label = self.label[idx]
img = cv.imread(image_path)
img = cv.cvtColor(img,cv.COLOR_BGR2RGB)
img = cv.resize(img, (220,220), interpolation = cv.INTER_AREA)#so all images be the same size
if self.transform:
img = self.transform(image = img)["image"]
return img , image_label
def __len__(self):
return len(self.img_paths)
def printImage(self,idx):
image_path = self.img_paths[idx]
image_label = self.label[idx]
img = cv.imread(image_path)
img = cv.cvtColor(img,cv.COLOR_BGR2RGB)
img = cv.resize(img, (220,220), interpolation = cv.INTER_AREA)
show(image_path,WantAxis=False,text=image_label)
#@title # Augmentation
transform = A.Compose([
OneOf([A.MedianBlur(blur_limit=9), A.Blur(blur_limit=8), A.GlassBlur(sigma=0.2, max_delta=2, p=0.4)],p=0.5),
A.GaussNoise (var_limit=(50.0, 150.0), mean=20, p=0.6),
A.Rotate(limit=45),
A.HorizontalFlip(p=0.5),
A.CLAHE (clip_limit=4.0, tile_grid_size=(8, 8), p=0.5),
A.RandomBrightnessContrast(brightness_limit=0.4, contrast_limit=0.3,p=0.3),
])
Gauss_Noise :
Adding Gaussian noise to the images can help make the face detection model more robust to real-world noise present in various environments. It introduces variations in pixel intensity, which can improve the model's ability to handle noisy input and enhance its generalization capability.
HorizontalFlip :
Flipping the images horizontally creates mirror images, effectively doubling the amount of training data. It helps the model learn to detect faces from different viewpoints and orientations, leading to improved accuracy and robustness.
RandomBrightnessContrast :
Randomly adjusting the brightness and contrast of the images adds diversity to the training data. This augmentation technique helps the model learn to detect faces under different lighting conditions, making it more adaptable to varying environments.
CLAHE (Contrast Limited Adaptive Histogram Equalization):
Applying CLAHE enhances the contrast of local regions within the image. It can improve the visibility of subtle facial details and features, enabling the model to better identify and detect faces.
MedianBlur:
Median blur is effective in reducing noise while preserving edges and fine details. This augmentation technique can help remove noise or artifacts from the images, making the face detection process more accurate and reliable.
#@title # Augmentations example
all_paths = nf['full_path'].values
all_names = nf['name'].values
img = plt.imread(all_paths[150])
label = all_names[150]
def showAug(img,name,transform_example):
example_img = transform_example(image = img)["image"]
show(example_img,WantAxis=False,text=name)
transform = A.Compose([A.Compose([A.GaussNoise (var_limit=(50.0, 150.0), mean=20, always_apply=True)])
,A.Compose([A.HorizontalFlip(always_apply=True)])
, A.Compose([A.RandomBrightnessContrast(brightness_limit=0.4, contrast_limit=0.3, always_apply=True)])
,A.Compose([A.CLAHE(clip_limit=4.0, tile_grid_size=(8, 8), always_apply=True, p=0.5)]),
A.Compose([A.MedianBlur(blur_limit=9, always_apply=True)])
])
Gauss_Noise = A.Compose([A.GaussNoise (var_limit=(50.0, 150.0), mean=20, always_apply=True)])
HorizontalFlip = A.Compose([A.HorizontalFlip(always_apply=True)])
RandomBrightnessContrast = A.Compose([A.RandomBrightnessContrast(brightness_limit=0.4, contrast_limit=0.3, always_apply=True)])
CLAHE = A.Compose([A.CLAHE(clip_limit=4.0, tile_grid_size=(8, 8), always_apply=True, p=0.5)])
median_blur = A.Compose([A.MedianBlur(blur_limit=9, always_apply=True)])
arr_aug = [Gauss_Noise,HorizontalFlip,RandomBrightnessContrast,CLAHE,median_blur];
#@title Original image
show(img,WantAxis=False,text=label)
#@title Gauss_Noise
showAug(img,label,arr_aug[0])
#@title HorizontalFlip
showAug(img,label,arr_aug[1])
#@title RandomBrightnessContrast
showAug(img,label,arr_aug[2])
#@title CLAHE
showAug(img,label,arr_aug[3])
#@title median_blur
showAug(img,label,arr_aug[4])
#@title ## Create DataLoader and divide to train and validation
dataset = FaceDataset(all_paths,all_names, transform)#creat instanse of FaceDataset
BS = 16
#split to 80% train 20% validation
indices = list(range(len(dataset)))
split = int(np.floor(0.2 * len(dataset)))
np.random.seed(seed=42)
np.random.shuffle(indices)
train_indices, val_indices = indices[split:], indices[:split]
# Creating PT data samplers and loaders:
train_sampler = SubsetRandomSampler(train_indices);
valid_sampler = SubsetRandomSampler(val_indices);
#create data loaders for train and validation
train_loader = DataLoader(dataset, batch_size=BS, num_workers=4, sampler=train_sampler);
validation_loader = DataLoader(dataset, batch_size=BS, num_workers=4, sampler=valid_sampler);
#@title Ready to work ! :)
x,y = next(iter(train_loader))
x = x.cpu().numpy()
# y = y.cpu().numpy()
plt.figure(figsize=[17,15])
for i,(row,t) in enumerate(zip(x,y)):
plt.subplot(4,4,i+1)
plt.axis("off")
plt.imshow(row)
plt.title(t)
plt.show()
def getImages():
img1 = cv.imread("/content/Screenshot_20230121_202601.jpg")
img2 = cv.imread("/content/Screenshot_20180122-172101_wo_500_500.png")
img2 = cv.resize(img2,(1080, 1253))
return img1.copy(),img2.copy()
def switch_face(img1, img2, face1, face2):
x1, y1, w1, h1 = face1
x2, y2, w2, h2 = face2
# Extract the face regions from the images
face1_img = img1[y1:y1+h1, x1:x1+w1]
face2_img = img2[y2:y2+h2, x2:x2+w2]
# Resize the face regions to match each other's size
face1_img_resized = cv.resize(face1_img, (w2, h2))
face2_img_resized = cv.resize(face2_img, (w1, h1))
# Swap the faces by replacing the corresponding regions in the images
img1[y1:y1+h1, x1:x1+w1] = face2_img_resized
img2[y2:y2+h2, x2:x2+w2] = face1_img_resized
img1 = cv.cvtColor(img1,cv.COLOR_BGR2RGB)
img2 = cv.cvtColor(img2,cv.COLOR_BGR2RGB)
# Return the modified images
return img1, img2
img1,img2 = getImages()
g = [img1,img2]
original = []
plt.figure(figsize=(20,9))
face_cascade = cv.CascadeClassifier(cv.data.haarcascades + 'haarcascade_frontalface_default.xml')
i=1
for augmanted_image in g:
image = augmanted_image
gray_image = cv.cvtColor(image,cv.COLOR_RGB2GRAY)
frame_gray = cv.equalizeHist(gray_image)
faces = face_cascade.detectMultiScale(frame_gray, 1.1, 4)
for (x, y, w, h) in faces:
cv.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)
normal = cv.cvtColor(image,cv.COLOR_BGR2RGB);
show(normal,WantAxis=False)
original.append(normal)
<Figure size 2000x900 with 0 Axes>
def detact_face(img, face_cascade):
# Convert into grayscale
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# Detect faces
faces = face_cascade.detectMultiScale(gray, 1.1, 4)
return faces[0]
img1,img2 = getImages();
face_cascade = cv.CascadeClassifier(cv.data.haarcascades + 'haarcascade_frontalface_default.xml');
face1 = detact_face(img1, face_cascade);
face2 = detact_face(img2, face_cascade);
img1,img2=switch_face(img1, img2, face1, face2);
create_subplots([original[0],original[1],img1,img2],['Dori','Neta','DoriNeta','NetaDori']);
#@title TL;DR
'''
In this exercise, I made a custom dataset and data loader for face detection using the images my class uploaded to the drive.
I applied some augmentations I had seen fit the provided data to imitate scenarios that can occur in photos that are taken on a day-to-day basis, I also presented the augmentations examples on the data.
After doing so, I divided the data into 20% validation and 80% train and created the data loaders.
In the second part, I used OpenCV to detect faces in an image, I also created a function that swapped faces between two images.
''';
%%shell
jupyter nbconvert --to html
File "<ipython-input-279-96ed913bdb4d>", line 2 jupyter nbconvert --to html your_notebook.ipynb ^ SyntaxError: invalid syntax